home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / nntpcdos.c < prev    next >
C/C++ Source or Header  |  1996-05-02  |  41KB  |  1,371 lines

  1. /*
  2.  * Program:    Network News Transfer Protocol (NNTP) client routines for DOS
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    25 January 1993
  13.  * Last Edited:    2 May 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys/stat.h>
  45. #undef LOCAL
  46. #include "smtp.h"
  47. #include "nntp.h"
  48. #include "nntpcdos.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51. #include "newsrc.h"
  52.  
  53. /* NNTP mail routines */
  54.  
  55.  
  56. /* Driver dispatch used by MAIL */
  57.  
  58. DRIVER nntpdriver = {
  59.   "nntp",            /* driver name */
  60.   (DRIVER *) NIL,        /* next driver */
  61.   nntp_valid,            /* mailbox is valid for us */
  62.   nntp_parameters,        /* manipulate parameters */
  63.   nntp_find,            /* find mailboxes */
  64.   nntp_find_bboards,        /* find bboards */
  65.   nntp_find_all,        /* find all mailboxes */
  66.   nntp_find_all_bboards,    /* find all bboards */
  67.   nntp_subscribe,        /* subscribe to mailbox */
  68.   nntp_unsubscribe,        /* unsubscribe from mailbox */
  69.   nntp_subscribe_bboard,    /* subscribe to bboard */
  70.   nntp_unsubscribe_bboard,    /* unsubscribe from bboard */
  71.   nntp_create,            /* create mailbox */
  72.   nntp_delete,            /* delete mailbox */
  73.   nntp_rename,            /* rename mailbox */
  74.   nntp_mopen,            /* open mailbox */
  75.   nntp_close,            /* close mailbox */
  76.   nntp_fetchfast,        /* fetch message "fast" attributes */
  77.   nntp_fetchflags,        /* fetch message flags */
  78.   nntp_fetchstructure,        /* fetch message envelopes */
  79.   nntp_fetchheader,        /* fetch message header only */
  80.   nntp_fetchtext,        /* fetch message body only */
  81.   nntp_fetchbody,        /* fetch message body section */
  82.   nntp_setflag,            /* set message flag */
  83.   nntp_clearflag,        /* clear message flag */
  84.   nntp_search,            /* search for message based on criteria */
  85.   nntp_ping,            /* ping mailbox to see if still alive */
  86.   nntp_check,            /* check for new messages */
  87.   nntp_expunge,            /* expunge deleted messages */
  88.   nntp_copy,            /* copy messages to another mailbox */
  89.   nntp_move,            /* move messages to another mailbox */
  90.   nntp_append,            /* append string message to mailbox */
  91.   nntp_gc            /* garbage collect stream */
  92. };
  93.  
  94.                 /* prototype stream */
  95. MAILSTREAM nntpproto = {&nntpdriver};
  96.  
  97. /* NNTP mail validate mailbox
  98.  * Accepts: mailbox name
  99.  * Returns: our driver if name is valid, NIL otherwise
  100.  */
  101.  
  102. DRIVER *nntp_valid (char *name)
  103. {
  104.                 /* must be bboard */
  105.   return *name == '*' ? mail_valid_net (name,&nntpdriver,NIL,NIL) : NIL;
  106. }
  107.  
  108.  
  109. /* News manipulate driver parameters
  110.  * Accepts: function code
  111.  *        function-dependent value
  112.  * Returns: function-dependent return value
  113.  */
  114.  
  115. void *nntp_parameters (long function,void *value)
  116. {
  117.   return NIL;
  118. }
  119.  
  120. /* NNTP mail find list of mailboxes
  121.  * Accepts: mail stream
  122.  *        pattern to search
  123.  */
  124.  
  125. void nntp_find (MAILSTREAM *stream,char *pat)
  126. {
  127.   /* Always a no-op */
  128. }
  129.  
  130.  
  131. /* NNTP mail find list of bboards
  132.  * Accepts: mail stream
  133.  *        pattern to search
  134.  */
  135.  
  136. void nntp_find_bboards (MAILSTREAM *stream,char *pat)
  137. {
  138.   char *t;
  139.   void *s = NIL;
  140.   if (stream) newsrc_find (pat);/* use .newsrc if a stream given */
  141.   else while (t = sm_read (&s))    /* use subscription manager if no stream */
  142.     if ((*t == '*') && pmatch (t+1,pat)) mm_bboard (t+1);
  143. }
  144.  
  145. /* NNTP mail find list of all mailboxes
  146.  * Accepts: mail stream
  147.  *        pattern to search
  148.  */
  149.  
  150. void nntp_find_all (MAILSTREAM *stream,char *pat)
  151. {
  152.   /* Always a no-op */
  153. }
  154.  
  155.  
  156. /* NNTP mail find list of all bboards
  157.  * Accepts: mail stream
  158.  *        pattern to search
  159.  */
  160.  
  161. void nntp_find_all_bboards (MAILSTREAM *stream,char *pat)
  162. {
  163.   char *s,*t,*bbd,*patx,tmp[MAILTMPLEN];
  164.                 /* use .newsrc if a stream given */
  165.   if (stream && LOCAL && LOCAL->nntpstream) {
  166.                 /* begin with a host specification? */
  167.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  168.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  169.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  170.       strcpy (tmp,pat);        /* copy host name */
  171.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  172.     }
  173.     else {            /* no host specification */
  174.       bbd = tmp;        /* no prefix */
  175.       patx = pat;        /* use entire specification */
  176.     }
  177.                 /* ask server for all active newsgroups */
  178.     if (!(nntp_send (LOCAL->nntpstream,"LIST","ACTIVE") == NNTPGLIST)) return;
  179.                 /* process data until we see final dot */
  180.     while ((s = tcp_getline (LOCAL->nntpstream->tcpstream)) && *s != '.') {
  181.                 /* tie off after newsgroup name */
  182.       if (t = strchr (s,' ')) *t = '\0';
  183.       if (pmatch (s,patx)) {    /* report to main program if have match */
  184.     strcpy (bbd,s);        /* write newsgroup name after prefix */
  185.     mm_bboard (tmp);
  186.       }
  187.       fs_give ((void **) &s);    /* clean up */
  188.     }
  189.   }
  190. }
  191.  
  192. /* NNTP mail subscribe to mailbox
  193.  * Accepts: mail stream
  194.  *        mailbox to add to subscription list
  195.  * Returns: T on success, NIL on failure
  196.  */
  197.  
  198. long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
  199. {
  200.   return NIL;            /* never valid for NNTP */
  201. }
  202.  
  203.  
  204. /* NNTP mail unsubscribe to mailbox
  205.  * Accepts: mail stream
  206.  *        mailbox to delete from subscription list
  207.  * Returns: T on success, NIL on failure
  208.  */
  209.  
  210. long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
  211. {
  212.   return NIL;            /* never valid for NNTP */
  213. }
  214.  
  215.  
  216. /* NNTP mail subscribe to bboard
  217.  * Accepts: mail stream
  218.  *        bboard to add to subscription list
  219.  * Returns: T on success, NIL on failure
  220.  */
  221.  
  222. long nntp_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  223. {
  224.   char *s = strchr (mailbox,'}');
  225.   return s ? newsrc_update (s+1,':') : NIL;
  226. }
  227.  
  228.  
  229. /* NNTP mail unsubscribe to bboard
  230.  * Accepts: mail stream
  231.  *        bboard to delete from subscription list
  232.  * Returns: T on success, NIL on failure
  233.  */
  234.  
  235. long nntp_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  236. {
  237.   char *s = strchr (mailbox,'}');
  238.   return s ? newsrc_update (s+1,'!') : NIL;
  239. }
  240.  
  241. /* NNTP mail create mailbox
  242.  * Accepts: mail stream
  243.  *        mailbox name to create
  244.  * Returns: T on success, NIL on failure
  245.  */
  246.  
  247. long nntp_create (MAILSTREAM *stream,char *mailbox)
  248. {
  249.   return NIL;            /* never valid for NNTP */
  250. }
  251.  
  252.  
  253. /* NNTP mail delete mailbox
  254.  *        mailbox name to delete
  255.  * Returns: T on success, NIL on failure
  256.  */
  257.  
  258. long nntp_delete (MAILSTREAM *stream,char *mailbox)
  259. {
  260.   return NIL;            /* never valid for NNTP */
  261. }
  262.  
  263.  
  264. /* NNTP mail rename mailbox
  265.  * Accepts: mail stream
  266.  *        old mailbox name
  267.  *        new mailbox name
  268.  * Returns: T on success, NIL on failure
  269.  */
  270.  
  271. long nntp_rename (MAILSTREAM *stream,char *old,char *new)
  272. {
  273.   return NIL;            /* never valid for NNTP */
  274. }
  275.  
  276. /* NNTP mail open
  277.  * Accepts: stream to open
  278.  * Returns: stream on success, NIL on failure
  279.  */
  280.  
  281. MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
  282. {
  283.   long i,j,k;
  284.   long nmsgs = 0;
  285.   long unseen = 0;
  286.   static int tmpcnt = 0;
  287.   char c = NIL,*s,*t,tmp[MAILTMPLEN];
  288.   NETMBX mb;
  289.   void *tcpstream;
  290.   SMTPSTREAM *nstream = NIL;
  291.                 /* return prototype for OP_PROTOTYPE call */
  292.   if (!stream) return &nntpproto;
  293.   mail_valid_net_parse (stream->mailbox,&mb);
  294.   if (!*mb.mailbox) strcpy (mb.mailbox,"general");
  295.   if (LOCAL) {            /* if recycle stream, see if changing hosts */
  296.     if (strcmp (lcase (mb.host),lcase (strcpy (tmp,LOCAL->host)))) {
  297.       sprintf (tmp,"Closing connection to %s",LOCAL->host);
  298.       if (!stream->silent) mm_log (tmp,(long) NIL);
  299.     }
  300.     else {            /* same host, preserve NNTP connection */
  301.       sprintf (tmp,"Reusing connection to %s",LOCAL->host);
  302.       if (!stream->silent) mm_log (tmp,(long) NIL);
  303.       nstream = LOCAL->nntpstream;
  304.       LOCAL->nntpstream = NIL;/* keep nntp_close() from punting it */
  305.     }
  306.     nntp_close (stream);    /* do close action */
  307.     stream->dtb = &nntpdriver;/* reattach this driver */
  308.     mail_free_cache (stream);    /* clean up cache */
  309.   }
  310.                 /* in case /debug switch given */
  311.   if (mb.dbgflag) stream->debug = T;
  312.  
  313.   if (!nstream) {        /* open NNTP now if not already open */
  314.     char *hostlist[2];
  315.     hostlist[0] = strcpy (tmp,mb.host);
  316.     if (mb.port) sprintf (tmp + strlen (tmp),":%ld",mb.port);
  317.     hostlist[1] = NIL;
  318.     nstream = nntp_open (hostlist,OP_READONLY+(stream->debug ? OP_DEBUG:NIL));
  319.   }
  320.   if (nstream) {        /* now try to open newsgroup */
  321.     if ((!stream->halfopen) &&    /* open the newsgroup if not halfopen */
  322.     ((nntp_send (nstream,"GROUP",mb.mailbox) != NNTPGOK) ||
  323.      ((nmsgs = strtol (nstream->reply + 4,&s,10)) < 0) ||
  324.      ((i = strtol (s,&s,10)) < 0) || ((j = strtol (s,&s,10)) < 0))) {
  325.       mm_log (nstream->reply,ERROR);
  326.       smtp_close (nstream);    /* punt stream */
  327.       nstream = NIL;
  328.       return NIL;
  329.     }
  330.                 /* newsgroup open, instantiate local data */
  331.     stream->local = fs_get (sizeof (NNTPLOCAL));
  332.     LOCAL->nntpstream = nstream;
  333.     LOCAL->dirty = NIL;        /* no update to .newsrc needed yet */
  334.                 /* copy host and newsgroup name */
  335.     LOCAL->host = cpystr (mb.host);
  336.     LOCAL->name = cpystr (mb.mailbox);
  337.     LOCAL->hm = LOCAL->tm = 0;    /* no cache header/text message numbers yet */
  338.     LOCAL->ht = NIL;        /* no header text yet */
  339.     if (!((s = getenv ("TMP")) || (s = getenv ("TEMP")))) s = myhomedir ();
  340.     if ((i = strlen (s)) && ((s[i-1] == '\\') || (s[i-1]=='/')))
  341.       s[i-1] = '\0';        /* tie off trailing directory delimiter */
  342.     sprintf (tmp,"%s\\NEWS%4.4d.TMP",s,++tmpcnt);
  343.     LOCAL->tf = cpystr (tmp);    /* build name of tmp file we use for text */
  344.     stream->sequence++;        /* bump sequence number */
  345.     stream->rdonly = T;        /* make sure higher level knows readonly */
  346.     LOCAL->number = NIL;
  347.  
  348.     if (!stream->halfopen) {    /* if not half-open */
  349.       if (nmsgs) {        /* find what messages exist */
  350.     LOCAL->number = (unsigned long *) fs_get (nmsgs*sizeof(unsigned long));
  351.     sprintf (tmp,"%ld-%ld",i,j);
  352.     if ((nntp_send (nstream,"LISTGROUP",mb.mailbox) == NNTPGOK) ||
  353.         (nntp_send (nstream,"XHDR Date",tmp) == NNTPHEAD)) {
  354.       for (i = 0; (s = tcp_getline (nstream->tcpstream)) && strcmp (s,".");
  355.            i++) {        /* initialize c-client/NNTP map */
  356.         if (i < nmsgs) LOCAL->number[i] = atol (s);
  357.         fs_give ((void **) &s);
  358.       }
  359.       if (s) fs_give ((void **) &s);
  360.       if (i != nmsgs) {    /* found holes? */
  361.         if (nmsgs = i)
  362.           fs_resize ((void **) &LOCAL->number,nmsgs*sizeof(unsigned long));
  363.         else fs_give ((void **) &LOCAL->number);
  364.       }
  365.     }
  366.                 /* assume c-client/NNTP map is entire range */
  367.     else for (k = 0; k < nmsgs; ++k) LOCAL->number[k] = i + k;
  368.       }
  369.       mail_exists(stream,nmsgs);/* notify upper level about mailbox size */
  370.                 /* read .newsrc entries */
  371.       mail_recent (stream,newsrc_read (LOCAL->name,stream,LOCAL->number));
  372.                 /* notify if empty bboard */
  373.       if (!(stream->nmsgs || stream->silent)) {
  374.     sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
  375.     mm_log (tmp,WARN);
  376.       }
  377.     }
  378.   }
  379.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  380. }
  381.  
  382. /* NNTP mail close
  383.  * Accepts: MAIL stream
  384.  */
  385.  
  386. void nntp_close (MAILSTREAM *stream)
  387. {
  388.   if (LOCAL) {            /* only if a file is open */
  389.     nntp_check (stream);    /* dump final checkpoint */
  390.     if (LOCAL->ht) fs_give ((void **) &LOCAL->ht);
  391.     unlink (LOCAL->tf);        /* sayonara to the text temporary file */
  392.     fs_give ((void **) &LOCAL->tf);
  393.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  394.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  395.     if (LOCAL->number) fs_give ((void **) &LOCAL->number);
  396.                 /* close NNTP connection */
  397.     if (LOCAL->nntpstream) smtp_close (LOCAL->nntpstream);
  398.                 /* nuke the local data */
  399.     fs_give ((void **) &stream->local);
  400.     stream->dtb = NIL;        /* log out the DTB */
  401.   }
  402. }
  403.  
  404. /* NNTP mail fetch fast information
  405.  * Accepts: MAIL stream
  406.  *        sequence
  407.  */
  408.  
  409. void nntp_fetchfast (MAILSTREAM *stream,char *sequence)
  410. {
  411.   long i;
  412.   BODY *b;
  413.                 /* ugly and slow */
  414.   if (stream && LOCAL && mail_sequence (stream,sequence))
  415.     for (i = 1; i <= stream->nmsgs; i++)
  416.       if (mail_elt (stream,i)->sequence)
  417.     nntp_fetchstructure (stream,i,&b);
  418. }
  419.  
  420.  
  421. /* NNTP mail fetch flags
  422.  * Accepts: MAIL stream
  423.  *        sequence
  424.  */
  425.  
  426. void nntp_fetchflags (MAILSTREAM *stream,char *sequence)
  427. {
  428.   return;            /* no-op for local mail */
  429. }
  430.  
  431. /* NNTP string driver for file stringstructs */
  432.  
  433. STRINGDRIVER nntp_string = {
  434.   nntp_string_init,        /* initialize string structure */
  435.   nntp_string_next,        /* get next byte in string structure */
  436.   nntp_string_setpos        /* set position in string structure */
  437. };
  438.  
  439.  
  440. /* Cache buffer for file stringstructs */
  441.  
  442. #define DOSCHUNKLEN 4096
  443. char dos_chunk[DOSCHUNKLEN];
  444.  
  445.  
  446. /* Initialize NNTP string structure for file stringstruct
  447.  * Accepts: string structure
  448.  *        pointer to string
  449.  *        size of string
  450.  */
  451.  
  452. void nntp_string_init (STRING *s,void *data,unsigned long size)
  453. {
  454.   NNTPDATA *d = (NNTPDATA *) data;
  455.   s->data = (void *) d->fd;    /* note fd */
  456.   s->data1 = d->pos;        /* note file offset */
  457.   s->size = size;        /* note size */
  458.   s->curpos = s->chunk = dos_chunk;
  459.   s->chunksize = (unsigned long) DOSCHUNKLEN;
  460.   s->offset = 0;        /* initial position */
  461.                 /* and size of data */
  462.   s->cursize = min ((long) DOSCHUNKLEN,size);
  463.                 /* move to that position in the file */
  464.   lseek (d->fd,d->pos,SEEK_SET);
  465.   read (d->fd,s->chunk,(unsigned int) s->cursize);
  466. }
  467.  
  468. /* Get next character from file stringstruct
  469.  * Accepts: string structure
  470.  * Returns: character, string structure chunk refreshed
  471.  */
  472.  
  473. char nntp_string_next (STRING *s)
  474. {
  475.   char c = *s->curpos++;    /* get next byte */
  476.                 /* move to next chunk */
  477.   SETPOS (s,s->offset + s->chunksize);
  478.   return c;            /* return the byte */
  479. }
  480.  
  481.  
  482. /* Set string pointer position for file stringstruct
  483.  * Accepts: string structure
  484.  *        new position
  485.  */
  486.  
  487. void nntp_string_setpos (STRING *s,unsigned long i)
  488. {
  489.   s->offset = i;        /* set new offset */
  490.   s->curpos = s->chunk;        /* reset position */
  491.                 /* set size of data */
  492.   if (s->cursize = s->size > s->offset ? min ((long) DOSCHUNKLEN,SIZE (s)):0) {
  493.                 /* move to that position in the file */
  494.     lseek ((int) s->data,s->data1 + s->offset,SEEK_SET);
  495.     read ((int) s->data,s->curpos,(unsigned int) s->cursize);
  496.   }
  497. }
  498.  
  499. /* NNTP mail fetch envelope
  500.  * Accepts: MAIL stream
  501.  *        message # to fetch
  502.  *        pointer to return body
  503.  * Returns: envelope of this message, body returned in body value
  504.  *
  505.  * Fetches the "fast" information as well
  506.  */
  507.  
  508. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  509.  
  510. ENVELOPE *nntp_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  511. {
  512.   char *h,tmp[MAXHDR];
  513.   LONGCACHE *lelt;
  514.   ENVELOPE **env;
  515.   STRING bs;
  516.   NNTPDATA d;
  517.   BODY **b;
  518.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  519.   unsigned long hdrsize;
  520.   unsigned long textsize = 0;
  521.   if (stream->scache) {        /* short cache */
  522.     if (msgno != stream->msgno){/* flush old poop if a different message */
  523.       mail_free_envelope (&stream->env);
  524.       mail_free_body (&stream->body);
  525.     }
  526.     stream->msgno = msgno;
  527.     env = &stream->env;        /* get pointers to envelope and body */
  528.     b = &stream->body;
  529.   }
  530.   else {            /* long cache */
  531.     lelt = mail_lelt (stream,msgno);
  532.     env = &lelt->env;        /* get pointers to envelope and body */
  533.     b = &lelt->body;
  534.   }
  535.  
  536.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  537.     mail_free_envelope (env);    /* flush old envelope and body */
  538.     mail_free_body (b);
  539.     if (!(h = nntp_fetchheader_work (stream,msgno,&hdrsize)))
  540.       mail_elt (stream,msgno)->deleted = T;
  541.     if (body) {            /* get message text */
  542.       if ((d.fd = nntp_fetchtext_work (stream,msgno,&textsize)) >= 0) {
  543.     d.pos = 0;        /* starting at beginning of file */
  544.     INIT (&bs,nntp_string,(void *) &d,textsize);
  545.                 /* calculate message size */
  546.     elt->rfc822_size = hdrsize + textsize;
  547.       }
  548.       else {            /* failed, mark as deleted */
  549.     mail_elt (stream,msgno)->deleted = T;
  550.     body = NIL;        /* failed or some reason */
  551.       }
  552.     }
  553.                 /* parse envelope and body */
  554.     rfc822_parse_msg (env,body ? b : NIL,h ? h : "",hdrsize,body ? &bs : NIL,
  555.               BADHOST,tmp);
  556.                 /* clean up the file descriptor */
  557.     if (body) close ((int) bs.data);
  558.                 /* parse date */
  559.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  560.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  561.   }
  562.   if (body) *body = *b;        /* return the body */
  563.   return *env;            /* return the envelope */
  564. }
  565.  
  566. /* NNTP mail fetch message header
  567.  * Accepts: MAIL stream
  568.  *        message # to fetch
  569.  * Returns: message header in RFC822 format
  570.  */
  571.  
  572. char *nntp_fetchheader (MAILSTREAM *stream,long msgno)
  573. {
  574.   char *ret;
  575.   unsigned long hdrsize;
  576.                 /* get the header */
  577.   if (!(ret = nntp_fetchheader_work (stream,msgno,&hdrsize)))
  578.     mail_elt (stream,msgno)->deleted = T;
  579.   return ret ? ret : "";
  580. }
  581.  
  582.  
  583. /* NNTP mail fetch message text (body only)
  584.  * Accepts: MAIL stream
  585.  *        message # to fetch
  586.  * Returns: message text in RFC822 format
  587.  */
  588.  
  589. char *nntp_fetchtext (MAILSTREAM *stream,long msgno)
  590. {
  591.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  592.   unsigned long textsize;
  593.   long m = msgno - 1;
  594.   char tmp[MAILTMPLEN];
  595.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  596.   if (stream->text) fs_give ((void **) &stream->text);
  597.                 /* get the text */
  598.   if ((LOCAL->fd = nntp_fetchtext_work (stream,msgno,&textsize)) >= 0) {
  599.                 /* return it to user */
  600.     stream->text = (mg ? *mg : mm_gets) (nntp_read,stream,textsize);
  601.     close (LOCAL->fd);        /* clean up the file descriptor */
  602.   }
  603.   else elt->deleted = T;    /* failed, mark as deleted */
  604.   elt->seen = T;        /* mark as seen */
  605.   return stream->text ? stream->text : "";
  606. }
  607.  
  608. /* NNTP fetch message body as a structure
  609.  * Accepts: Mail stream
  610.  *        message # to fetch
  611.  *        section specifier
  612.  *        pointer to length
  613.  * Returns: pointer to section of message body
  614.  */
  615.  
  616. char *nntp_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  617. {
  618.   BODY *b;
  619.   PART *pt;
  620.   unsigned long i;
  621.   unsigned long offset = 0;
  622.   unsigned long textsize;
  623.   MESSAGECACHE *elt = mail_elt (stream,m);
  624.   char tmp[MAILTMPLEN];
  625.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  626.   if (stream->text) fs_give ((void **) &stream->text);
  627.                 /* make sure have a body */
  628.   if (!(nntp_fetchstructure (stream,m,&b) && b && s && *s &&
  629.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  630.   do {                /* until find desired body part */
  631.                 /* multipart content? */
  632.     if (b->type == TYPEMULTIPART) {
  633.       pt = b->contents.part;    /* yes, find desired part */
  634.       while (--i && (pt = pt->next));
  635.       if (!pt) return NIL;    /* bad specifier */
  636.                 /* note new body, check valid nesting */
  637.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  638.       offset = pt->offset;    /* get new offset */
  639.     }
  640.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  641.                 /* need to go down further? */
  642.     if (i = *s) switch (b->type) {
  643.     case TYPEMESSAGE:        /* embedded message, calculate new offset */
  644.       offset = b->contents.msg.offset;
  645.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  646.     case TYPEMULTIPART:        /* multipart, get next section */
  647.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  648.     default:            /* bogus subpart specification */
  649.       return NIL;
  650.     }
  651.   } while (i);
  652.                 /* lose if body bogus */
  653.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  654.   if ((LOCAL->fd = nntp_fetchtext_work (stream,m,&textsize)) >= 0) {
  655.                 /* move to that place in the file */
  656.     lseek (LOCAL->fd,offset,SEEK_SET);
  657.     stream->text =        /* return it to user */
  658.       (mg ? *mg : mm_gets) (nntp_read,stream,*len = b->size.ibytes);
  659.     close (LOCAL->fd);        /* clean up the file descriptor */
  660.   }
  661.   else elt->deleted = T;    /* failed, mark as deleted */
  662.   elt->seen = T;        /* mark as seen */
  663.   return stream->text ? stream->text : "";
  664. }
  665.  
  666. /* NNTP mail fetch message header worker routine
  667.  * Accepts: MAIL stream
  668.  *        message # to fetch
  669.  *        pointer to size to return
  670.  * Returns: file descriptor of header temp file or -1 if error
  671.  */
  672.  
  673. char *nntp_fetchheader_work (MAILSTREAM *stream,long msgno,unsigned long *size)
  674. {
  675.   int fd;
  676.   char *s,*t;
  677.   unsigned long i,j;
  678.   unsigned long bufsize = MAILTMPLEN;
  679.   if (msgno != LOCAL->hm) {    /* if message number different */
  680.     if (LOCAL->ht) fs_give ((void **) &LOCAL->ht);
  681.     LOCAL->hs = 0;        /* no size yet */
  682.     LOCAL->ht = (char *) fs_get (bufsize);
  683.     LOCAL->hm = 0;        /* may get an error getting message */
  684.     sprintf (LOCAL->ht,"%ld",LOCAL->number[msgno - 1]);
  685.     if (nntp_send (LOCAL->nntpstream,"HEAD",LOCAL->ht) != NNTPHEAD) return NIL;
  686.     LOCAL->hm = msgno;        /* note have cached message now */
  687.     while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  688.       if (*s == '.') {        /* possible end of text? */
  689.     if (s[1]) t = s + 1;    /* pointer to true start of line */
  690.     else {
  691.       fs_give ((void **) &s);
  692.       break;        /* end of data */
  693.     }
  694.       }
  695.       else t = s;        /* want the entire line */
  696.                 /* ensure have enough room */
  697.       if ((j = (LOCAL->hs + (i = strlen (t))) + 5) >= bufsize)
  698.     fs_resize ((void **) &LOCAL->ht,
  699.            bufsize += MAILTMPLEN * ((j / MAILTMPLEN) + 1));
  700.                 /* copy the text */
  701.       strncpy (LOCAL->ht + LOCAL->hs,t,i);
  702.       LOCAL->hs += i;        /* set new buffer position */
  703.       LOCAL->ht[LOCAL->hs++] = '\015';
  704.       LOCAL->ht[LOCAL->hs++] = '\012';
  705.       fs_give ((void **) &s);    /* free the line */
  706.     }
  707.                 /* add final newline */
  708.     LOCAL->ht[LOCAL->hs++] = '\015';
  709.     LOCAL->ht[LOCAL->hs++] = '\012';
  710.     LOCAL->ht[LOCAL->hs] = '\0';/* tie off string with NUL */
  711.   }
  712.   *size = LOCAL->hs;        /* return header size */
  713.   return LOCAL->ht;
  714. }
  715.  
  716. /* NNTP file open
  717.  * Accepts: file name
  718.  *        access mode
  719.  *        pointer to size to return
  720.  * Returns: file descriptor, or -1 if failure
  721.  */
  722.  
  723. int nntp_fopen (char *file,int access,unsigned long *size)
  724. {
  725.   int fd = open (file,O_BINARY | access,S_IREAD|S_IWRITE);
  726.   if (fd < 0) {
  727.     char tmp[MAILTMPLEN];
  728.     sprintf (tmp,"Can't open scratch file %s",file);
  729.     mm_log (tmp,ERROR);
  730.     *size = 0;            /* zap the size then */
  731.   }
  732.   else {            /* get the file size */
  733.     struct stat sbuf;
  734.     fstat (fd,&sbuf);
  735.     *size = sbuf.st_size;
  736.   }
  737.   return fd;
  738. }
  739.  
  740.  
  741. /* NNTP mail read
  742.  * Accepts: MAIL stream
  743.  *        number of bytes to read
  744.  *        buffer address
  745.  * Returns: T if success, NIL otherwise
  746.  */
  747.  
  748. long nntp_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  749. {
  750.   return read (LOCAL->fd,buffer,(unsigned int) count) ? T : NIL;
  751. }
  752.  
  753. /* NNTP mail fetch message text (body only)
  754.  * Accepts: MAIL stream
  755.  *        message # to fetch
  756.  *        pointer to size to return
  757.  * Returns: file descriptor of header temp file or -1 if error
  758.  */
  759.  
  760. int nntp_fetchtext_work (MAILSTREAM *stream,long msgno,unsigned long *size)
  761. {
  762.   char *s,*t,tmp[MAILTMPLEN];
  763.   int i,fd;
  764.   if (msgno == LOCAL->tm) return nntp_fopen (LOCAL->tf,O_RDONLY,size);
  765.   LOCAL->tm = 0;        /* get the header */
  766.   sprintf (tmp,"%ld",LOCAL->number[msgno - 1]);
  767.   if (nntp_send (LOCAL->nntpstream,"BODY",tmp) != NNTPBODY) return -1;
  768.   LOCAL->tm = msgno;        /* remember this message number */
  769.   fd = nntp_fopen (LOCAL->tf,O_RDWR|O_TRUNC|O_CREAT,size);
  770.   while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  771.     if (*s == '.') {        /* possible end of text? */
  772.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  773.       else {
  774.     fs_give ((void **) &s);    /* free the line */
  775.     break;            /* end of data */
  776.       }
  777.     }
  778.     else t = s;            /* want the entire line */
  779.     if (fd >= 0) {        /* copy it to the file */
  780.       if ((write (fd,t,i = strlen (t)) >= 0) &&
  781.       (write (fd,"\015\012",2) >= 0))
  782.     *size += i + 2;        /* tally up size of data */
  783.       else {
  784.     sprintf (tmp,"Error writing scratch file at byte %lu",*size);
  785.     mm_log (tmp,ERROR);
  786.     close (fd);        /* forget it */
  787.     fd = -1;        /* failure now */
  788.       }
  789.     }
  790.     fs_give ((void **) &s);    /* free the line */
  791.   }
  792.   if (fd >= 0) {        /* making a file? */
  793.     write (fd,"\015\012",2);
  794.     *size += 2;            /* write final newline */
  795.                 /* rewind to start of file */
  796.     lseek (fd,(unsigned long) 0,SEEK_SET);
  797.   }
  798.   return fd;            /* return the file descriptor */
  799. }
  800.  
  801. /* NNTP mail set flag
  802.  * Accepts: MAIL stream
  803.  *        sequence
  804.  *        flag(s)
  805.  */
  806.  
  807. void nntp_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  808. {
  809.   MESSAGECACHE *elt;
  810.   long i;
  811.   short f = nntp_getflags (stream,flag);
  812.   if (!f) return;        /* no-op if no flags to modify */
  813.                 /* get sequence and loop on it */
  814.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  815.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  816.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  817.       if (f&fDELETED) {        /* deletion marks it in newsrc */
  818.     elt->deleted = T;    /* mark deleted */
  819.     LOCAL->dirty = T;    /* mark dirty */
  820.       }
  821.       if (f&fFLAGGED) elt->flagged = T;
  822.       if (f&fANSWERED) elt->answered = T;
  823.     }
  824. }
  825.  
  826.  
  827. /* NNTP mail clear flag
  828.  * Accepts: MAIL stream
  829.  *        sequence
  830.  *        flag(s)
  831.  */
  832.  
  833. void nntp_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  834. {
  835.   MESSAGECACHE *elt;
  836.   long i;
  837.   short f = nntp_getflags (stream,flag);
  838.   if (!f) return;        /* no-op if no flags to modify */
  839.                 /* get sequence and loop on it */
  840.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  841.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  842.                 /* clear all requested flags */
  843.       if (f&fSEEN) elt->seen = NIL;
  844.       if (f&fDELETED) {
  845.     elt->deleted = NIL;    /* undelete */
  846.     LOCAL->dirty = T;    /* mark stream as dirty */
  847.       }
  848.       if (f&fFLAGGED) elt->flagged = NIL;
  849.       if (f&fANSWERED) elt->answered = NIL;
  850.     }
  851. }
  852.  
  853. /* NNTP mail search for messages
  854.  * Accepts: MAIL stream
  855.  *        search criteria
  856.  */
  857.  
  858. void nntp_search (MAILSTREAM *stream,char *criteria)
  859. {
  860.   long i,n;
  861.   char *d,tmp[MAILTMPLEN];
  862.   search_t f;
  863.                 /* initially all searched */
  864.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  865.                 /* get first criterion */
  866.   if (criteria && (criteria = strtok (criteria," "))) {
  867.                 /* for each criterion */
  868.     for (; criteria; (criteria = strtok (NIL," "))) {
  869.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  870.       switch (*ucase (criteria)) {
  871.       case 'A':            /* possible ALL, ANSWERED */
  872.     if (!strcmp (criteria+1,"LL")) f = nntp_search_all;
  873.     else if (!strcmp (criteria+1,"NSWERED")) f = nntp_search_answered;
  874.     break;
  875.       case 'B':            /* possible BCC, BEFORE, BODY */
  876.     if (!strcmp (criteria+1,"CC"))
  877.       f = nntp_search_string (nntp_search_bcc,&d,&n);
  878.     else if (!strcmp (criteria+1,"EFORE"))
  879.       f = nntp_search_date (nntp_search_before,&n);
  880.     else if (!strcmp (criteria+1,"ODY"))
  881.       f = nntp_search_string (nntp_search_body,&d,&n);
  882.     break;
  883.       case 'C':            /* possible CC */
  884.     if (!strcmp (criteria+1,"C")) 
  885.       f = nntp_search_string (nntp_search_cc,&d,&n);
  886.     break;
  887.       case 'D':            /* possible DELETED */
  888.     if (!strcmp (criteria+1,"ELETED")) f = nntp_search_deleted;
  889.     break;
  890.       case 'F':            /* possible FLAGGED, FROM */
  891.     if (!strcmp (criteria+1,"LAGGED")) f = nntp_search_flagged;
  892.     else if (!strcmp (criteria+1,"ROM"))
  893.       f = nntp_search_string (nntp_search_from,&d,&n);
  894.     break;
  895.       case 'K':            /* possible KEYWORD */
  896.     if (!strcmp (criteria+1,"EYWORD"))
  897.       f = nntp_search_flag (nntp_search_keyword,&d);
  898.     break;
  899.       case 'N':            /* possible NEW */
  900.     if (!strcmp (criteria+1,"EW")) f = nntp_search_new;
  901.     break;
  902.  
  903.       case 'O':            /* possible OLD, ON */
  904.     if (!strcmp (criteria+1,"LD")) f = nntp_search_old;
  905.     else if (!strcmp (criteria+1,"N"))
  906.       f = nntp_search_date (nntp_search_on,&n);
  907.     break;
  908.       case 'R':            /* possible RECENT */
  909.     if (!strcmp (criteria+1,"ECENT")) f = nntp_search_recent;
  910.     break;
  911.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  912.     if (!strcmp (criteria+1,"EEN")) f = nntp_search_seen;
  913.     else if (!strcmp (criteria+1,"INCE"))
  914.       f = nntp_search_date (nntp_search_since,&n);
  915.     else if (!strcmp (criteria+1,"UBJECT"))
  916.       f = nntp_search_string (nntp_search_subject,&d,&n);
  917.     break;
  918.       case 'T':            /* possible TEXT, TO */
  919.     if (!strcmp (criteria+1,"EXT"))
  920.       f = nntp_search_string (nntp_search_text,&d,&n);
  921.     else if (!strcmp (criteria+1,"O"))
  922.       f = nntp_search_string (nntp_search_to,&d,&n);
  923.     break;
  924.       case 'U':            /* possible UN* */
  925.     if (criteria[1] == 'N') {
  926.       if (!strcmp (criteria+2,"ANSWERED")) f = nntp_search_unanswered;
  927.       else if (!strcmp (criteria+2,"DELETED")) f = nntp_search_undeleted;
  928.       else if (!strcmp (criteria+2,"FLAGGED")) f = nntp_search_unflagged;
  929.       else if (!strcmp (criteria+2,"KEYWORD"))
  930.         f = nntp_search_flag (nntp_search_unkeyword,&d);
  931.       else if (!strcmp (criteria+2,"SEEN")) f = nntp_search_unseen;
  932.     }
  933.     break;
  934.       default:            /* we will barf below */
  935.     break;
  936.       }
  937.       if (!f) {            /* if can't determine any criteria */
  938.     sprintf (tmp,"Unknown search criterion: %.80s",criteria);
  939.     mm_log (tmp,ERROR);
  940.     return;
  941.       }
  942.                 /* run the search criterion */
  943.       for (i = 1; i <= stream->nmsgs; ++i)
  944.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  945.       mail_elt (stream,i)->searched = NIL;
  946.     }
  947.                 /* report search results to main program */
  948.     for (i = 1; i <= stream->nmsgs; ++i)
  949.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  950.   }
  951. }
  952.  
  953. /* NNTP mail ping mailbox
  954.  * Accepts: MAIL stream
  955.  * Returns: T if stream alive, else NIL
  956.  */
  957.  
  958. long nntp_ping (MAILSTREAM *stream)
  959. {
  960.   /* Kludge alert: SMTPSOFTFATAL is 421 which is used in NNTP to mean ``No
  961.    * next article in this group''.  Hopefully, no NNTP server will send this
  962.    * in response to a STAT */
  963.   return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != SMTPSOFTFATAL);
  964. }
  965.  
  966.  
  967. /* NNTP mail check mailbox
  968.  * Accepts: MAIL stream
  969.  */
  970.  
  971. void nntp_check (MAILSTREAM *stream)
  972. {
  973.                 /* never do if no updates */
  974.   if (LOCAL->dirty) newsrc_write (LOCAL->name,stream,LOCAL->number);
  975.   LOCAL->dirty = NIL;
  976. }
  977.  
  978. /* NNTP mail expunge mailbox
  979.  * Accepts: MAIL stream
  980.  */
  981.  
  982. void nntp_expunge (MAILSTREAM *stream)
  983. {
  984.   if (!stream->silent)
  985.     mm_log ("Expunge ignored on readonly mailbox",(long) NIL);
  986. }
  987.  
  988.  
  989. /* NNTP mail copy message(s)
  990.  * Accepts: MAIL stream
  991.  *        sequence
  992.  *        destination mailbox
  993.  * Returns: T if copy successful, else NIL
  994.  */
  995.  
  996. long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  997. {
  998.   mm_log ("Copy not valid for NNTP",ERROR);
  999.   return NIL;
  1000. }
  1001.  
  1002. /* NNTP mail move message(s)
  1003.  * Accepts: MAIL stream
  1004.  *        sequence
  1005.  *        destination mailbox
  1006.  * Returns: T if move successful, else NIL
  1007.  */
  1008.  
  1009. long nntp_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1010. {
  1011.   mm_log ("Move not valid for NNTP",ERROR);
  1012.   return NIL;
  1013. }
  1014.  
  1015.  
  1016. /* NNTP mail append message from stringstruct
  1017.  * Accepts: MAIL stream
  1018.  *        destination mailbox
  1019.  *        stringstruct of messages to append
  1020.  * Returns: T if append successful, else NIL
  1021.  */
  1022.  
  1023. long nntp_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  1024.           STRING *message)
  1025. {
  1026.   mm_log ("Append not valid for NNTP",ERROR);
  1027.   return NIL;
  1028. }
  1029.  
  1030.  
  1031. /* NNTP garbage collect stream
  1032.  * Accepts: Mail stream
  1033.  *        garbage collection flags
  1034.  */
  1035.  
  1036. void nntp_gc (MAILSTREAM *stream,long gcflags)
  1037. {
  1038.   /* nothing for now */
  1039. }
  1040.  
  1041. /* Internal routines */
  1042.  
  1043.  
  1044. /* Parse flag list
  1045.  * Accepts: MAIL stream
  1046.  *        flag list as a character string
  1047.  * Returns: flag command list
  1048.  */
  1049.  
  1050. short nntp_getflags (MAILSTREAM *stream,char *flag)
  1051. {
  1052.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  1053.   short f = 0;
  1054.   short i,j;
  1055.   if (flag && *flag) {        /* no-op if no flag string */
  1056.                 /* check if a list and make sure valid */
  1057.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1058.       mm_log ("Bad flag list",ERROR);
  1059.       return NIL;
  1060.     }
  1061.                 /* copy the flag string w/o list construct */
  1062.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1063.     tmp[j] = '\0';
  1064.     t = ucase (tmp);        /* uppercase only from now on */
  1065.  
  1066.     while (t && *t) {        /* parse the flags */
  1067.       if (*t == '\\') {        /* system flag? */
  1068.     switch (*++t) {        /* dispatch based on first character */
  1069.     case 'S':        /* possible \Seen flag */
  1070.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1071.       t += 4;        /* skip past flag name */
  1072.       break;
  1073.     case 'D':        /* possible \Deleted flag */
  1074.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1075.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1076.       t += 7;        /* skip past flag name */
  1077.       break;
  1078.     case 'F':        /* possible \Flagged flag */
  1079.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1080.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1081.       t += 7;        /* skip past flag name */
  1082.       break;
  1083.     case 'A':        /* possible \Answered flag */
  1084.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1085.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1086.       t += 8;        /* skip past flag name */
  1087.       break;
  1088.     default:        /* unknown */
  1089.       i = 0;
  1090.       break;
  1091.     }
  1092.                 /* add flag to flags list */
  1093.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1094.       }
  1095.       else {            /* no user flags yet */
  1096.     t = strtok (t," ");    /* isolate flag name */
  1097.     sprintf (err,"Unknown flag: %.80s",t);
  1098.     t = strtok (NIL," ");    /* get next flag */
  1099.     mm_log (err,ERROR);
  1100.       }
  1101.     }
  1102.   }
  1103.   return f;
  1104. }
  1105.  
  1106. /* Search support routines
  1107.  * Accepts: MAIL stream
  1108.  *        message number
  1109.  *        pointer to additional data
  1110.  *        pointer to temporary buffer
  1111.  * Returns: T if search matches, else NIL
  1112.  */
  1113.  
  1114. char nntp_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1115. {
  1116.   return T;            /* ALL always succeeds */
  1117. }
  1118.  
  1119.  
  1120. char nntp_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1121. {
  1122.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1123. }
  1124.  
  1125.  
  1126. char nntp_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1127. {
  1128.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1129. }
  1130.  
  1131.  
  1132. char nntp_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1133. {
  1134.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1135. }
  1136.  
  1137.  
  1138. char nntp_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1139. {
  1140.   return NIL;            /* keywords not supported yet */
  1141. }
  1142.  
  1143.  
  1144. char nntp_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1145. {
  1146.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1147.   return (elt->recent && !elt->seen) ? T : NIL;
  1148. }
  1149.  
  1150. char nntp_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1151. {
  1152.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1153. }
  1154.  
  1155.  
  1156. char nntp_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1157. {
  1158.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1159. }
  1160.  
  1161.  
  1162. char nntp_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1163. {
  1164.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1165. }
  1166.  
  1167.  
  1168. char nntp_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1169. {
  1170.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1171. }
  1172.  
  1173.  
  1174. char nntp_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1175. {
  1176.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1177. }
  1178.  
  1179.  
  1180. char nntp_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1181. {
  1182.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1183. }
  1184.  
  1185.  
  1186. char nntp_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1187. {
  1188.   return T;            /* keywords not supported yet */
  1189. }
  1190.  
  1191.  
  1192. char nntp_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1193. {
  1194.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1195. }
  1196.  
  1197. char nntp_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1198. {
  1199.   return (char) (nntp_msgdate (stream,msgno) < n);
  1200. }
  1201.  
  1202.  
  1203. char nntp_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1204. {
  1205.   return (char) (nntp_msgdate (stream,msgno) == n);
  1206. }
  1207.  
  1208.  
  1209. char nntp_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1210. {
  1211.                 /* everybody interprets "since" as .GE. */
  1212.   return (char) (nntp_msgdate (stream,msgno) >= n);
  1213. }
  1214.  
  1215.  
  1216. unsigned long nntp_msgdate (MAILSTREAM *stream,long msgno)
  1217. {
  1218.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1219.                 /* get date if don't have it yet */
  1220.   if (!elt->day) nntp_fetchstructure (stream,msgno,NIL);
  1221.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1222. }
  1223.  
  1224. #define BUFLEN 4*MAILTMPLEN
  1225.  
  1226. char nntp_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1227. {
  1228.   char tmp[BUFLEN];
  1229.   unsigned long bufsize,textsize,curpos = 0;
  1230.                 /* get the text */
  1231.   if ((LOCAL->fd = nntp_fetchtext_work (stream,msgno,&textsize)) >= 0) {
  1232.     while (textsize) {
  1233.       bufsize = min (textsize,(unsigned long) BUFLEN);
  1234.       read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1235.       if (search (tmp,bufsize,d,n)) return T;
  1236.                 /* backtrack by pattern size if not at end */
  1237.       if (bufsize != textsize) bufsize -= n;
  1238.       textsize -= bufsize;    /* this many bytes handled */
  1239.       curpos += bufsize;    /* advance to that point */
  1240.       lseek (LOCAL->fd,curpos,SEEK_SET);
  1241.     }
  1242.     close (LOCAL->fd);        /* clean up the file descriptor */
  1243.   }
  1244.   return NIL;            /* not found */
  1245. }
  1246.  
  1247.  
  1248. char nntp_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1249. {
  1250.   char *t = nntp_fetchstructure (stream,msgno,NIL)->subject;
  1251.   return t ? search (t,(unsigned long) strlen (t),d,n) : NIL;
  1252. }
  1253.  
  1254.  
  1255. char nntp_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1256. {
  1257.   char *s;
  1258.   unsigned long hdrsize;
  1259.                 /* get the header */
  1260.   return ((s = nntp_fetchheader_work (stream,msgno,&hdrsize)) &&
  1261.       (search (s,hdrsize,d,n))) ? T : nntp_search_body (stream,msgno,d,n);
  1262. }
  1263.  
  1264. char nntp_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1265. {
  1266.   char tmp[8*MAILTMPLEN];
  1267.   tmp[0] = '\0';        /* initially empty string */
  1268.                 /* get text for address */
  1269.   rfc822_write_address (tmp,nntp_fetchstructure(stream,msgno,NIL)->bcc);
  1270.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1271. }
  1272.  
  1273.  
  1274. char nntp_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1275. {
  1276.   char tmp[8*MAILTMPLEN];
  1277.   tmp[0] = '\0';        /* initially empty string */
  1278.                 /* get text for address */
  1279.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->cc);
  1280.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1281. }
  1282.  
  1283.  
  1284. char nntp_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1285. {
  1286.   char tmp[8*MAILTMPLEN];
  1287.   tmp[0] = '\0';        /* initially empty string */
  1288.                 /* get text for address */
  1289.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->from);
  1290.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1291. }
  1292.  
  1293.  
  1294. char nntp_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1295. {
  1296.   char tmp[8*MAILTMPLEN];
  1297.   tmp[0] = '\0';            /* initially empty string */
  1298.                 /* get text for address */
  1299.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->to);
  1300.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1301. }
  1302.  
  1303. /* Search parsers */
  1304.  
  1305.  
  1306. /* Parse a date
  1307.  * Accepts: function to return
  1308.  *        pointer to date integer to return
  1309.  * Returns: function to return
  1310.  */
  1311.  
  1312. search_t nntp_search_date (search_t f,long *n)
  1313. {
  1314.   long i;
  1315.   char *s;
  1316.   MESSAGECACHE elt;
  1317.                 /* parse the date and return fn if OK */
  1318.   return (nntp_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1319.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1320. }
  1321.  
  1322. /* Parse a flag
  1323.  * Accepts: function to return
  1324.  *        pointer to string to return
  1325.  * Returns: function to return
  1326.  */
  1327.  
  1328. search_t nntp_search_flag (search_t f,char **d)
  1329. {
  1330.                 /* get a keyword, return if OK */
  1331.   return (*d = strtok (NIL," ")) ? f : NIL;
  1332. }
  1333.  
  1334.  
  1335. /* Parse a string
  1336.  * Accepts: function to return
  1337.  *        pointer to string to return
  1338.  *        pointer to string length to return
  1339.  * Returns: function to return
  1340.  */
  1341.  
  1342. search_t nntp_search_string (search_t f,char **d,long *n)
  1343. {
  1344.   char *end = " ";
  1345.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1346.   if (!c) return NIL;        /* missing argument */
  1347.   switch (*c) {            /* see what the argument is */
  1348.   case '{':            /* literal string */
  1349.     *n = strtol (c+1,d,10);    /* get its length */
  1350.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1351.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1352.       char e = *--c;
  1353.       *c = DELIM;        /* make sure not a space */
  1354.       strtok (c," ");        /* reset the strtok mechanism */
  1355.       *c = e;            /* put character back */
  1356.       break;
  1357.     }
  1358.   case '\0':            /* catch bogons */
  1359.   case ' ':
  1360.     return NIL;
  1361.   case '"':            /* quoted string */
  1362.     if (strchr (c+1,'"')) end = "\"";
  1363.     else return NIL;
  1364.   default:            /* atomic string */
  1365.     if (*d = strtok (c,end)) *n = strlen (*d);
  1366.     else return NIL;
  1367.     break;
  1368.   }
  1369.   return f;
  1370. }
  1371.